iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 17
1
Modern Web

從Stack Overflow學前端系列 第 17

從StackOverflow上學CODING(17)如何用JS得知DOM元素出現在目前視窗

  • 分享至 

  • xImage
  •  

How to tell if a DOM element is visible in the current viewport?

Is there an efficient way to tell if a DOM element (in an HTML document) is currently visible (appears in the viewport)?
有沒有有效的方法得知哪些DOM元素(html)是目前正出現在視窗內的?


Now most browsers support getBoundingClientRect method, which has become the best practice. Using an old answer is very slow, not accurate and has several bugs.
The solution selected as correct is almost never precise. You can read more about its bugs.
This solution was tested on IE7+, iOS5+ Safari, Android2+, Blackberry, Opera Mobile, and IE Mobile 10.
目前多數的瀏覽器都支持getBoundingClientRect方法,同時也是最佳的用法,用其他舊的方法很慢也有很多bug.
這個方法也在IE7+, iOS5+ Safari, Android2+, Blackberry, Opera Mobile,IE Mobile 10驗證過

function isElementInViewport (el) {

    //special bonus for those using jQuery
    if (typeof jQuery === "function" && el instanceof jQuery) {
        el = el[0];
    }

    var rect = el.getBoundingClientRect();

    return (
        rect.top >= 0 &&
        rect.left >= 0 &&
        rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && /*or $(window).height() */
        rect.right <= (window.innerWidth || document.documentElement.clientWidth) /*or $(window).width() */
    );
}

How to use:
You can be sure that the function given above returns correct answer at the moment of time when it is called, but what about tracking element's visibility as an event?
Place the following code at the bottom of your tag:
如何使用:
你可以放心確認這個函式會在被呼叫時的瞬間取回正確的答案,但是如何追蹤一個元素是可視時的事件?
請試著在標籤下使用以下的碼

function onVisibilityChange(el, callback) {
    var old_visible;
    return function () {
        var visible = isElementInViewport(el);
        if (visible != old_visible) {
            old_visible = visible;
            if (typeof callback == 'function') {
                callback();
            }
        }
    }
}

var handler = onVisibilityChange(el, function() {
    /* your code go here */
});


//jQuery
$(window).on('DOMContentLoaded load resize scroll', handler); 

/* //non-jQuery
if (window.addEventListener) {
    addEventListener('DOMContentLoaded', handler, false); 
    addEventListener('load', handler, false); 
    addEventListener('scroll', handler, false); 
    addEventListener('resize', handler, false); 
} else if (window.attachEvent)  {
    attachEvent('onDOMContentLoaded', handler); // IE9+ :(
    attachEvent('onload', handler);
    attachEvent('onscroll', handler);
    attachEvent('onresize', handler);
}

*/
If you do any DOM modifications, they can change your element's visibility of course.

Guidelines and common pitfalls:

Maybe you need to track page zoom / mobile device pinch? jQuery should handle zoom/pinch cross browser, otherwise first or second link should help you.

If you modify DOM, it can affect the element's visibility. You should take control over that and call handler() manually. Unfortunately, we have no cross browser onrepaint event. On the other hand that allows us to make optimizations and perform re-check only on DOM modifications that can change element's visibility.
如果你想做任何的DOM修改,這些的確可以改變看不看的到元素,
你可能會需要找頁面的區域大小,jQuery比較好在擴瀏覽器時掌握這個方面,不然第一或第二個連結會比較能幫助到你,
如果你修改了DOM,這就可能會改變元素的可視性,你同時必須要主動去控制call handler,不幸的是我們沒有跨瀏覽器的事件handler,另一方面這也允許我們最佳化效能還有能檢查會改變DOM元素的可視性的修改

Never Ever use it inside jQuery $(document).ready() only, because there is no warranty CSS has been applied in this moment. Your code can work locally with your CSS on hard drive, but once put on remote server it will fail.

After DOMContentLoaded is fired, styles are applied, but the images are not loaded yet. So, we should add window.onload event listener.

We can't catch zoom/pinch event yet.
最好不要單獨在jQuery $(document).ready()用這個方法,因為不能保證css一定會抓到,其css很有可能只會在本地被應用到,然而在遠端伺服器的時候則很有可能失敗
DOM內容被載入時樣式會被套用,但是有時候圖案還不會被載入,所以記得要加入window.onload的事件監聽:
The last resort could be the following code:

/* TODO: this looks like a very bad code */
setInterval(handler, 600); 

上一篇
從StackOverflow上學CODING(16)陣列內使用delete與splice的差別?
下一篇
從StackOverflow上學CODING(18)宣告Array()與[]的差異
系列文
從Stack Overflow學前端30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言